/*
    日志器 & 日志器建造者 模块
    1. 抽象出日志器基类
    2. 派生出同步日志器和异步日志器
    3. 抽象出日志器建造者基类，简化日志器的使用
    4. 派生出局部建造者和全局建造者，建造者都可以生产同步和异步日志器
*/

#pragma once

#include "util.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

namespace easylog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string& logger_name, 
               LogLevel::value level,
               Formatter::ptr& formatter,
               std::vector<LogSink::ptr>& sinks)
               :_logger_name(logger_name), _limit_level(level),
               _formatter(formatter), _sinks(sinks)
               {}
        //获取日志器名称
        const std::string& getLoggerName() { return _logger_name; }
        //提供不同日志等级的日志输出接口
        //构造日志消息对象，并格式化，然后进行日志输出
        void debug(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(!shouldLog(LogLevel::value::DEBUG)) return;
            //取出不定参数
            va_list ap;
            va_start(ap, fmt);
            serialization(LogLevel::value::DEBUG, file, line, fmt, ap);
            va_end(ap);
        }
        void info(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(!shouldLog(LogLevel::value::INFO)) return;
            //取出不定参数
            va_list ap;
            va_start(ap, fmt);
            serialization(LogLevel::value::INFO, file, line, fmt, ap);
            va_end(ap);
        }
        void warn(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(!shouldLog(LogLevel::value::WARN)) return;
            //取出不定参数
            va_list ap;
            va_start(ap, fmt);
            serialization(LogLevel::value::WARN, file, line, fmt, ap);
            va_end(ap);
        }
        void error(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(!shouldLog(LogLevel::value::ERROR)) return;
            //取出不定参数
            va_list ap;
            va_start(ap, fmt);
            serialization(LogLevel::value::ERROR, file, line, fmt, ap);
            va_end(ap);
        }
        void fatal(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(!shouldLog(LogLevel::value::FATAL)) return;
            //取出不定参数
            va_list ap;
            va_start(ap, fmt);
            serialization(LogLevel::value::FATAL, file, line, fmt, ap);
            va_end(ap);
        }
    protected:
        //构造日志消息类，格式化日志消息，实际落地
        void serialization(LogLevel::value level, const std::string& file, size_t line, const std::string& fmt, va_list& ap)
        {
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
                throw except("logger.hpp: Logger serialization vasprintf failed!");
            //构造日志消息类
            LogMsg msg(_logger_name, file, line, level, res);
            //格式化日志消息
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        //判断该日志等级的消息是否需要输出
        bool shouldLog(LogLevel::value level) { return level >= _limit_level; }
        //抽象接口完成实际的落地输出--不同日志器会有不同的落地方法
        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex;//互斥锁
        std::string _logger_name;//日志器名称
        Formatter::ptr _formatter;//格式器
        std::vector<LogSink::ptr> _sinks;//落地方向数组
        //因其频繁访问，有需线程安全，固使用原子变量，保证线程安全
        std::atomic<LogLevel::value> _limit_level;//限制等级
    };

    //同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string& logger_name,
                   LogLevel::value level, 
                   Formatter::ptr& formatter,
                   std::vector<LogSink::ptr>& sinks)
                   :Logger(logger_name, level, formatter, sinks)
                   {}
    protected:
        void log(const char* data, size_t len) override
        {
            //加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty()) return;
            for(auto& sink : _sinks)
                sink->log(data, len);
        }
    };
    //异步日志器
    class AsyncLogger : public Logger
    {
    public: 
        AsyncLogger(const std::string& logger_name,
                   LogLevel::value level, 
                   Formatter::ptr& formatter,
                   std::vector<LogSink::ptr>& sinks,
                   ReserveType bufferType)
                   :Logger(logger_name, level, formatter, sinks),
                   _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), 
                           bufferType))
                   {
                        std::cout<< "异步日志器创建成功\n";
                   }
    protected:
        //重写基类纯虚函数
        void log(const char* data, size_t len) override
        {
            _looper->push(data, len);
        }
        //提供给异步工作器的处理函数
        void realLog(Buffer& buffer)
        {
            std::cout << "realLog" << std::endl;
            //因为有this指针，所以可以访问到AsyncLogger的成员
            for(auto& sink : _sinks)//不需要加锁保护，因为异步工作器是单执行流，不需要保护线程安全
                sink->log(buffer.readBegin(), buffer.readAbleSize());
        }
    private:
        AsyncLooper::ptr _looper;
    };

    //日志器类型
    enum class LoggerType
    {
        LOGGER_SYNC,//同步
        LOGGER_ASYNC//异步
    };
    //日志器建造者基类:简化日志器的使用
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
                    :_limit_level(LogLevel::value::DEBUG),//默认限制等级为DEBUG
                    _logger_type(LoggerType::LOGGER_SYNC),//默认日志器类型为同步日志器
                    _bufferType(ReserveType::CLOG)//默认缓冲区扩展模式为阻塞
                    {}
        //设置日志器类型
        void buildLoggerType(LoggerType logger_type) { _logger_type = logger_type; }
        //设置日志器名称
        void buildLoggerName(const std::string& logger_name) { _logger_name = logger_name; }
        //设置日志器限制等级
        void buildLoggerLevel(LogLevel::value limit_level) { _limit_level = limit_level; }
        //构造格式化器
        void buildFormatter(const std::string& pattern) {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        //设置缓冲区扩容模式
        void setUnsafeAsync() { _bufferType = ReserveType::RESERVE; }
        //创建落地方向并添加到数组
        template<typename SinkType, typename ...Args>
        void buildSink(Args&& ...args)
        {
            LogSink::ptr lsptr = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(lsptr);
        }
        //整合和组织各组件并生成日志器
        virtual Logger::ptr build() = 0;
    protected:
        LoggerType _logger_type;//日志器类型
        std::string _logger_name;//日志器名称
        LogLevel::value _limit_level;//限制等级
        Formatter::ptr _formatter;//格式化器
        std::vector<LogSink::ptr> _sinks;//落地方向数组
        ReserveType _bufferType;//缓冲区扩容模式，默认为阻塞模式
    };
    //局部日志器建造者
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            if(_logger_name.empty())//日志器名称不能为空
                throw except("日志器名称为空");
            if(_formatter.get() == nullptr)//若无格式化器，使用默认格式
                _formatter = std::make_shared<Formatter>();
            if(_sinks.empty())//若无落地方向，默认为标准输出
                buildSink<StdOutSink>();
            if(_logger_type == LoggerType::LOGGER_ASYNC)
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _bufferType);
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };
    //日志器管理器——懒汉单例模式
    class LoggerManager
    {
    public:
        //获取静态日志器管理器对象
        static LoggerManager& getInstance()
        {
            //C++11后保证静态对象的创建是线程安全的
            //如果静态对象没有创建完，其他线程进入是会阻塞的
            static LoggerManager eton;
            return eton;
        }
        //添加日志器
        void addLogger(Logger::ptr logger)
        {
            if(hasLogger(logger->getLoggerName())) 
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert({logger->getLoggerName(), logger});
        }
        //判断是否有指定名称的日志器
        bool hasLogger(const std::string& logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())  
                return false;
            return true;
        }
        //获取指定名称的日志器
        Logger::ptr getLogger(const std::string& logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())
                return Logger::ptr();
            return it->second;
        }
        //获取默认日志器
        Logger::ptr rootLogger() { return _root_logger; }
    private:
        LoggerManager()
        {
            //此处的建造者不能使用全局建造者，会陷入死循环
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert({"root", _root_logger});
        }
    private:
        std::mutex _mutex;//互斥锁
        std::unordered_map<std::string, Logger::ptr> _loggers;//管理的日志器的哈希表
        Logger::ptr _root_logger;//默认日志器
    };
    //全局日志器建造者
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            if(_logger_name.empty())//日志器名称不能为空
                throw except("日志器名称为空");
            if(_formatter.get() == nullptr)//若无格式化器，使用默认格式
                _formatter = std::make_shared<Formatter>();
            if(_sinks.empty())//若无落地方向，默认为标准输出
                buildSink<StdOutSink>();
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _bufferType);
            else
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            //直接添加到日志器管理器
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        } 
    };
}